package bblrtc

import (
	"encoding/json"
	mux "github.com/julienschmidt/httprouter"
	"io/ioutil"
	"log"
	"net/http"
	"strings"
	"time"
)

func render(v interface{}, w http.ResponseWriter) {
	if b, err := json.Marshal(v); err == nil {
		w.Write(b)
		return
	} else {
		log.Println(err)
	}
}

func internal_error(rw http.ResponseWriter, req *http.Request, _ interface{}) {

	http.Error(rw, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
}
func forbidden(rw http.ResponseWriter, req *http.Request) {

	http.Error(rw, http.StatusText(http.StatusForbidden), http.StatusForbidden)
}

func badRequest(rw http.ResponseWriter, req *http.Request) {

	http.Error(rw, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
}

func reply(rw http.ResponseWriter, r *http.Request, code int) {
	http.Error(rw, err_code[code], code)
	sid := r.Header.Get("X-Sid")
	member := r.Header.Get("x-member")
	token := r.Header.Get("x-key")
	deviceId := r.Header.Get("x-device-id")
	syslog.Println(r.RemoteAddr, r.Header.Get("X-Forwarded-For")+"-"+r.Header.Get("X-Real-IP"), r.RequestURI, sid, deviceId, member, token, code, err_code[code])
}

func (s *HttpServer) on_register(w http.ResponseWriter, r *http.Request, _ mux.Params) {
	defer r.Body.Close()
	member := r.Header.Get("x-member")
	token := r.Header.Get("x-key")
	if !user_auth(member, token) {
		reply(w, r, ERR_AUTH)
		return
	}
	user := UDB.FindUser(member)
	if user != nil {
		reply(w, r, ERR_USER_EXIST)
		return
	}
	err := UDB.AddUser(member)
	if err != nil {
		reply(w, r, ERR_INTERNAL)
		return
	}
	reply(w, r, OK)
}

func (s *HttpServer) on_unregister(w http.ResponseWriter, r *http.Request, _ mux.Params) {
	defer r.Body.Close()
	member := r.Header.Get("x-member")
	token := r.Header.Get("x-key")
	if !user_auth(member, token) {
		reply(w, r, ERR_AUTH)
		return
	}
	user := UDB.FindUser(member)
	if user == nil {
		reply(w, r, ERR_USER_NOT_EXIST)
		return
	}
	reply(w, r, OK)
}

func (s *HttpServer) on_login(w http.ResponseWriter, r *http.Request, _ mux.Params) {
	var sid = "-"
	defer r.Body.Close()
	member := r.Header.Get("x-member")
	token := r.Header.Get("x-key")
	deviceId := r.Header.Get("x-device-id")
	if !user_auth(member, token) {
		reply(w, r, ERR_AUTH)
		return
	}
	ss, err := s.router.Register(member, deviceId, s.NodeId)
	if err != OK {
		reply(w, r, err)
	} else {
		servers := strings.Split(GetString("ice", "servers", ""), ",")
		body := parser_iceserver(servers)
		b, _ := json.Marshal(body)
		w.Header().Add("x-sid", ss.Sid)
		w.Header().Add("x-node", ss.NodeId)
		w.Header().Add("x-code", "OK")
		sid = ss.Sid
		w.Write(b)
	}
	syslog.Println(r.RemoteAddr, r.Header.Get("X-Forwarded-For")+"-"+r.Header.Get("X-Real-IP"), r.RequestURI, sid, deviceId, member, token, "OK")

}

func parser_iceserver(servers []string) []map[string]interface{} {
	list := []map[string]interface{}{}
	for _, server := range servers {
		if strings.HasPrefix(server, "stun") || strings.HasPrefix(server, "turn") {
			m := map[string]interface{}{"uri": server}
			list = append(list, m)
		} else {
			ss := strings.Split(server, "@")
			if len(ss) != 2 {
				continue
			}
			us := strings.Split(ss[0], ":")
			m := map[string]interface{}{"uri": ss[1], "username": us[0], "password": us[1]}
			list = append(list, m)
		}
	}
	return list
}

func (s *HttpServer) on_logout(w http.ResponseWriter, r *http.Request, _ mux.Params) {
	defer r.Body.Close()
	sid := r.Header.Get("X-Sid")
	err := s.router.Unregister(sid)
	if err != OK {
		reply(w, r, err)
	} else {
		reply(w, r, OK)
	}
}

func (s *HttpServer) on_offer(w http.ResponseWriter, r *http.Request, _ mux.Params) {
	var sid, devid, member = "-", "-", "-"
	var msg *Message
	defer func() {
		r.Body.Close()
	}()
	sid = r.Header.Get("X-Sid")
	if sid == "" {
		reply(w, r, ERR_SID)
		return
	}

	msg, err := readMessage(r)
	if err != nil {
		reply(w, r, ERR_IO)
		return
	}
	if msg.MsgId == "" || msg.To == "" {
		reply(w, r, ERR_PARAM)
		return
	}

	ss, code := s.router.Offer(sid, msg)
	if code != OK {
		reply(w, r, code)
	} else {
		devid = ss.DeviceId
		member = ss.Member
		w.Header().Set("x-caller-sid", ss.Sid)
		w.Header().Set("x-code", "OK")
		syslog.Println(r.RemoteAddr, r.Header.Get("X-Forwarded-For")+"-"+r.Header.Get("X-Real-IP"), r.RequestURI, sid, devid, member, "OK", msg)
	}
}

func (s *HttpServer) on_candidate(w http.ResponseWriter, r *http.Request, _ mux.Params) {
	var sid, devid, member = "-", "-", "-"
	var msg *Message
	defer r.Body.Close()
	sid = r.Header.Get("X-Sid")
	if sid == "" {
		reply(w, r, ERR_SID)
		return
	}
	msg, err := readMessage(r)
	if err != nil {
		reply(w, r, ERR_IO)
		return
	}
	if msg.MsgId == "" {
		reply(w, r, ERR_PARAM)
		return
	}

	if msg.CallerSID == "" {
		msg.CallerSID = sid
	}
	ss, code := s.router.Candidate(sid, msg)
	if code != OK {
		reply(w, r, code)
	} else {
		devid = ss.DeviceId
		member = ss.Member
		w.Header().Set("x-caller-sid", ss.Sid)
		w.Header().Set("x-code", "OK")
		syslog.Println(r.RemoteAddr, r.Header.Get("X-Forwarded-For")+"-"+r.Header.Get("X-Real-IP"), r.RequestURI, sid, devid, member, "OK", msg)
	}
}

func (s *HttpServer) on_answer(w http.ResponseWriter, r *http.Request, _ mux.Params) {
	var sid, devid, member = "-", "-", "-"
	var msg *Message
	defer r.Body.Close()
	sid = r.Header.Get("X-Sid")
	if sid == "" {
		reply(w, r, ERR_SID)
		return
	}
	msg, err := readMessage(r)
	if err != nil {
		reply(w, r, ERR_IO)
		return
	}

	if msg.MsgId == "" || msg.CallerSID == "" {
		reply(w, r, ERR_PARAM)
		return
	}
	ss, code := s.router.Answer(sid, msg)
	if code != OK {
		reply(w, r, code)
	} else {
		devid = ss.DeviceId
		member = ss.Member
		w.Header().Set("x-caller-sid", ss.Sid)
		w.Header().Set("x-code", "OK")
		syslog.Println(r.RemoteAddr, r.Header.Get("X-Forwarded-For")+"-"+r.Header.Get("X-Real-IP"), r.RequestURI, sid, devid, member, "OK", msg)
	}
}

func (s *HttpServer) on_bye(w http.ResponseWriter, r *http.Request, _ mux.Params) {
	var sid, devid, member = "-", "-", "-"
	defer r.Body.Close()
	sid = r.Header.Get("X-Sid")
	if sid == "" {
		reply(w, r, ERR_SID)
		return
	}

	msg, err := readMessage(r)
	if err != nil {
		reply(w, r, ERR_IO)
		return
	}

	if msg.MsgId == "" {
		reply(w, r, ERR_PARAM)
		return
	}

	if msg.CallerSID == "" {
		msg.CallerSID = sid
	}
	ss, code := s.router.Bye(sid, msg)
	if code != OK {
		reply(w, r, code)
	} else {
		devid = ss.DeviceId
		member = ss.Member
		w.Header().Set("x-caller-sid", ss.Sid)
		w.Header().Set("x-code", "OK")
		syslog.Println(r.RemoteAddr, r.Header.Get("X-Forwarded-For")+"-"+r.Header.Get("X-Real-IP"), r.RequestURI, sid, devid, member, "OK")
	}
}

// func (s *HttpServer) on_ack(w http.ResponseWriter, r *http.Request, _ mux.Params) {
// 	var sid, devid, member = "-", "-", "-"
// 	defer r.Body.Close()
// 	sid = r.Header.Get("X-Sid")
// 	if sid == "" {
// 		reply(w, r, ERR_SID)
// 		return
// 	}

// 	msg, err := readMessage(r)
// 	if err != nil {
// 		reply(w, r, ERR_IO)
// 		return
// 	}

// 	if msg.MsgId == "" {
// 		reply(w, r, ERR_PARAM)
// 		return
// 	}

// 	if msg.CallerSID == "" {
// 		msg.CallerSID = sid
// 	}
// 	ss, code := s.router.Ack(sid, msg)
// 	if code != OK {
// 		reply(w, r, code)
// 	} else {
// 		devid = ss.DeviceId
// 		member = ss.Member
// 		w.Header().Set("x-caller-sid", ss.Sid)
// 		w.Header().Set("x-code", "OK")
// 		syslog.Println(r.RemoteAddr, r.Header.Get("X-Forwarded-For")+"-"+r.Header.Get("X-Real-IP"), r.RequestURI, sid, devid, member, "OK")
// 	}
// }

func readMessage(r *http.Request) (*Message, error) {
	msg := &Message{}
	msg.MsgId = r.Header.Get("X-Msg-Id")
	msg.From = r.Header.Get("X-From")
	msg.To = r.Header.Get("X-To")
	msg.CallerSID = r.Header.Get("X-Caller-Sid")

	if b, e := ioutil.ReadAll(r.Body); e != nil {
		return nil, e
	} else {
		msg.Text = string(b)
	}
	return msg, nil
}

func (s *HttpServer) on_wait(w http.ResponseWriter, r *http.Request, _ mux.Params) {
	var sid, devid, member, tail = "-", "-", "-", []interface{}{}
	var ss *Peer
	defer func() {
		r.Body.Close()
		if ss != nil {
			ss.Gone()
		}
		syslog.Println(r.RemoteAddr, r.Header.Get("X-Forwarded-For")+"-"+r.Header.Get("X-Real-IP"), r.RequestURI, sid, devid, member, tail)
	}()
	sid = r.Header.Get("X-Sid")
	now := time.Now()
	if sid == "" {
		reply(w, r, ERR_SID)
		return
	}
	ss = s.router.FindPeerWithSid(sid)
	if ss == nil {
		reply(w, r, ERR_SNF)
		return
	}
	ss.Coming()
	//lmsg_id := r.Header.Get("X-Msg-Id")
	devid = ss.DeviceId
	member = ss.Member
	// if ss.last_msg != nil && lmsg_id != "" && ss.last_msg.MsgId != lmsg_id {
	// 	// ss.last_msg.SentTime = now
	// 	err := send_message(w, r, []*Message{ss.last_msg})
	// 	tail = []interface{}{lmsg_id, ss.last_msg.MsgId, err}
	// 	return
	// }
	cn, _ := w.(http.CloseNotifier)
	select {
	case <-cn.CloseNotify():
		tail = []interface{}{"client closed", now}
	case <-ss.KickedNotify():
		tail = []interface{}{"client kicked", now}
		reply(w, r, ERR_KICKED)
		//ss.Close()
	case msg, more := <-ss.channel:
		if !more {
			tail = []interface{}{"channel closed", now}
		} else {
			//msg.SentTime = now
			err := send_message(w, r, []*Message{msg})
			tail = []interface{}{msg.MsgId, err}
			ss.last_msg = msg
		}
	case <-time.After(time.Second * 30):
		reply(w, r, ERR_TIMEOUT)
		ss.Close()
	}
}

func send_message(w http.ResponseWriter, r *http.Request, msg []*Message) error {
	b, err := json.Marshal(msg)
	if err != nil {
		reply(w, r, ERR_IO)
		return err
	}
	w.Header().Add("x-code", "OK")
	w.Header().Add("X-Send-Time", Datetime())
	_, err = w.Write(b)
	syslog.Println("send", msg, err)
	return err
}
